#ifndef V2_NIM_API_H
#define V2_NIM_API_H
#include "v2_nim_ai_service.hpp"
#include "v2_nim_chatroom_service.hpp"
#include "v2_nim_conversation_group_service.hpp"
#include "v2_nim_conversation_service.hpp"
#include "v2_nim_friend_service.hpp"
#include "v2_nim_login_service.hpp"
#include "v2_nim_message_service.hpp"
#include "v2_nim_notification_service.hpp"
#include "v2_nim_setting_service.hpp"
#include "v2_nim_signalling_service.hpp"
#include "v2_nim_storage_service.hpp"
#include "v2_nim_team_service.hpp"
#include "v2_nim_user_service.hpp"
#ifdef WIN32
#include "wtypes.h"
#else
#include <dlfcn.h>
#endif
#ifdef ENABLE_NIM_V1
#include "nim_wrapper_util/nim_sdk_util.h"
namespace nim {
extern nim::SDKInstance* nim_sdk_instance;
}  // namespace nim
#endif
namespace v2 {
class V2NIMClient {
public:
    /// @brief 获取 IM 客户端实例
    /// @return V2NIMClient&
    /// @par 示例代码
    /// @code
    /// auto& client = V2NIMClient::get();
    /// @endcode
    static V2NIMClient& get();

    /// @brief 初始化
    /// @param option 初始化选项
    /// @return nstd::optional<V2NIMError>
    /// @par 示例代码
    /// @code
    /// V2NIMInitOption option;
    /// option.appkey = "your app key";
    /// option.appDataPath = "your app name";
    /// auto error = client.init(option);
    /// if (error) {
    ///     // init failed
    ///     // ...
    ///     return;
    /// }
    /// @endcode
    nstd::optional<V2NIMError> init(const V2NIMInitOption& option) {
        auto error = initImpl(option);
#ifdef ENABLE_NIM_V1
        if (!error && !nim::nim_sdk_instance) {
            nim::nim_sdk_instance = new nim::SDKInstance();
            if (!nim::nim_sdk_instance->LoadSdkDll("", internal::NIM_SDK_NAME)) {
                delete nim::nim_sdk_instance;
                nim::nim_sdk_instance = nullptr;
                throw std::runtime_error("[V2NIMClient] load v1 sdk failed");
            }
        }
#endif
        return error;
    }

    /// @internal
    virtual nstd::optional<V2NIMError> initImpl(const V2NIMInitOption& option) = 0;

    /// @brief 反初始化
    /// @return nstd::optional<V2NIMError>
    /// @par 示例代码
    /// @code
    /// auto error = client.uninit();
    /// if (error) {
    ///     // uninit failed
    ///     // ...
    ///     return;
    /// }
    /// @endcode
    nstd::optional<V2NIMError> uninit() {
        auto error = uninitImpl();
#ifdef ENABLE_NIM_V1
        if (!error && nim::nim_sdk_instance) {
            nim::nim_sdk_instance->UnLoadSdkDll();
            delete nim::nim_sdk_instance;
            nim::nim_sdk_instance = nullptr;
        }
#endif
        return error;
    }

    /// @internal
    virtual nstd::optional<V2NIMError> uninitImpl() = 0;

    /// @brief 获取登录服务
    /// @return V2NIMLoginService&
    /// @par 示例代码
    /// @code
    /// auto& loginService = client.getLoginService();
    /// @endcode
    virtual V2NIMLoginService& getLoginService() const = 0;

    /// @brief 获取会话服务
    /// @return V2NIMConversationService&
    /// @par 示例代码
    /// @code
    /// auto& conversationService = client.getConversationService();
    /// @endcode
    virtual V2NIMConversationService& getConversationService() const = 0;

    /// @brief 获取会话分组服务
    /// @return V2NIMConversationGroupService&
    /// @par 示例代码
    /// @code
    /// auto& conversationGroupService = client.getConversationGroupService();
    /// @endcode
    virtual V2NIMConversationGroupService& getConversationGroupService() const = 0;

    /// @brief 获取消息服务
    /// @return V2NIMMessageService&
    /// @par 示例代码
    /// @code
    /// auto& messageService = client.getMessageService();
    /// @endcode
    virtual V2NIMMessageService& getMessageService() const = 0;

    /// @brief 获取通知服务
    /// @return V2NIMNotificationService&
    /// @par 示例代码
    /// @code
    /// auto& notificationService = client.getNotificationService();
    /// @endcode
    virtual V2NIMNotificationService& getNotificationService() const = 0;

    /// @brief 获取存储服务
    /// @return V2NIMStorageService&
    /// @par 示例代码
    /// @code
    /// auto& storageService = client.getStorageService();
    /// @endcode
    virtual V2NIMStorageService& getStorageService() const = 0;

    /// @brief 获取群组服务
    /// @return V2NIMTeamService&
    /// @par 示例代码
    /// @code
    /// auto& teamService = client.getTeamService();
    /// @endcode
    virtual V2NIMTeamService& getTeamService() const = 0;

    /// @brief 获取设置服务
    /// @return V2NIMSettingService&
    /// @par 示例代码
    /// @code
    /// auto& settingService = client.getSettingService();
    /// @endcode
    virtual V2NIMSettingService& getSettingService() const = 0;

    /// @brief 获取用户资料服务
    /// @return V2NIMUserService&
    /// @par 示例代码
    /// @code
    /// auto& userService = client.getUserService();
    /// @endcode
    virtual V2NIMUserService& getUserService() const = 0;

    /// @brief 获取好友服务
    /// @return V2NIMFriendService&
    /// @par 示例代码
    /// @code
    /// auto& friendService = client.getFriendService();
    /// @endcode
    virtual V2NIMFriendService& getFriendService() const = 0;

    /// @brief 获取 AI 服务
    /// @return V2NIMAIService&
    /// @par 示例代码
    /// @code
    /// auto& aiService = client.getAIService();
    /// @endcode
    virtual V2NIMAIService& getAIService() const = 0;

    /// @brief 获取信令服务
    /// @return V2NIMSignallingService&
    /// @par 示例代码
    /// @code
    /// auto& signallingService = client.getSignallingService();
    /// @endcode
    virtual V2NIMSignallingService& getSignallingService() const = 0;
};

class V2NIMChatroomClient {
public:
    virtual ~V2NIMChatroomClient() = default;
    /// @brief 初始化
    /// @param option 初始化选项
    /// @return nstd::optional<V2NIMError>
    /// @par 示例代码
    /// @code
    /// V2NIMInitOption option;
    /// option.appkey = "your app key";
    /// option.appDataPath = "your app name";
    /// auto error = V2NIMChatroomClient::init(option);
    /// if (error) {
    ///     // init failed
    ///     // ...
    ///     return;
    /// }
    /// @endcode
    static nstd::optional<V2NIMError> init(const V2NIMInitOption& option);

    /// @brief 反初始化
    /// @return void
    /// @warning 请在退出程序前调用此方法
    /// @par 示例代码
    /// @code
    /// V2NIMChatroomClient::uninit();
    /// @endcode
    static void uninit();

    /// @brief 创建聊天室客户端实例
    /// @return nstd::shared_ptr<V2NIMChatroomClient>
    /// @pre 必须在调用此方法前调用 V2NIMChatroomClient::init
    /// @note 建议保存持有实例 ID 而不是实例本身
    /// @par 示例代码
    /// @code
    /// auto chatroomClient = V2NIMChatroomClient::newInstance();
    /// if (!chatroomClient) {
    ///     // create instance failed
    ///     // ...
    ///     return;
    /// }
    /// auto instanceId = chatroomClient->getInstanceId();
    /// // save instanceId to cache
    /// // ...
    /// @endcode
    static nstd::shared_ptr<V2NIMChatroomClient> newInstance();

    /// @brief 销毁聊天室客户端实例
    /// @param instanceId 聊天室客户端实例 ID
    /// @return void
    /// @pre 必须在调用此方法前调用 V2NIMChatroomClient::init
    /// @warning 严禁在调用此方法后访问对应的聊天室客户端实例
    /// @par 示例代码
    /// @code
    /// uint32_t instanceId{0};
    /// // get instanceId from cache
    /// // ...
    /// V2NIMChatroomClient::destroyInstance(instanceId);
    /// @endcode
    static void destroyInstance(uint32_t instanceId);

    /// @brief 获取聊天室客户端实例
    /// @return nstd::shared_ptr<V2NIMChatroomClient>
    /// @pre 必须在调用此方法前调用 V2NIMChatroomClient::init
    /// @par 示例代码
    /// @code
    /// uint32_t instanceId{0};
    /// // get instanceId from cache
    /// // ...
    /// auto chatroomClient = V2NIMChatroomClient::getInstance(instanceId);
    /// if (!chatroomClient) {
    ///     // instance not found
    ///     // ...
    ///     return;
    /// }
    /// @endcode
    static nstd::shared_ptr<V2NIMChatroomClient> getInstance(uint32_t instanceId);

    /// @brief 获取聊天室实例列表
    /// @return nstd::vector<nstd::shared_ptr<V2NIMChatroomClient>>
    /// @pre 必须在调用此方法前调用 V2NIMChatroomClient::init
    /// @par 示例代码
    /// @code
    /// auto chatroomClients = V2NIMChatroomClient::getInstanceList();
    /// @endcode
    static nstd::vector<nstd::shared_ptr<V2NIMChatroomClient>> getInstanceList();

    /// @brief 销毁所有聊天室客户端实例
    /// @return void
    /// @pre 必须在调用此方法前调用 V2NIMChatroomClient::init
    /// @warning 严禁在调用此方法后访问任何聊天室客户端实例, 此方法会退出聊天室, 耗时可能较长
    /// @par 示例代码
    /// @code
    /// V2NIMChatroomClient::destroyAll();
    /// @endcode
    static void destroyAll();

    /// @brief 获取聊天室客户端实例 ID
    /// @return uint32_t
    /// @par 示例代码
    /// @code
    /// auto& chatroomClient = V2NIMChatroomClient::newInstance();
    /// auto instanceId = chatroomClient.getInstanceId();
    /// @endcode
    virtual uint32_t getInstanceId() = 0;

    /// @brief 进入聊天室
    /// @param roomId 聊天室 ID
    /// @param enterParams 进入聊天室相关参数
    /// @param success 进入聊天室成功回调
    /// @param failure 进入聊天室失败回调
    /// @return void
    /// @par 示例代码
    /// @code
    /// V2NIMChatroomEnterParams enterParams;
    /// enterParams.accountId = "accountId";
    /// enterParams.token = "token";
    /// enterParams.roomNick = "nick";
    /// enterParams.roomAvatar = "avatar";
    /// enterParams.linkProvider = [](nstd::string roomId, nstd::string account) {
    ///     nstd::vector<nstd::string> linkAddresses;
    ///     // get link addresses
    ///     // ...
    ///     return linkAddresses;
    /// };
    /// enterParams.serverExtension = "server extension";
    /// enterParams.notificationExtension = "notification extension";
    /// chatroomClient.enter(
    ///     "roomId",
    ///     enterParams,
    ///     [](V2NIMChatroomEnterResult result) {
    ///         // enter succeeded
    ///     },
    ///     [](V2NIMError error) {
    ///         // enter failed, handle error
    ///     });
    /// @endcode
    virtual void enter(nstd::string roomId,
        V2NIMChatroomEnterParams enterParams,
        V2NIMSuccessCallback<V2NIMChatroomEnterResult> success,
        V2NIMFailureCallback failure) = 0;

    /// @brief 退出聊天室
    /// @return void
    /// @par 示例代码
    /// @code
    /// chatroomClient.exit();
    /// @endcode
    virtual void exit() = 0;

    /// @brief 查询聊天室信息
    /// @return V2NIMChatroomInfo
    /// @par 示例代码
    /// @code
    /// auto chatroomInfo = chatroomClient.getChatroomInfo();
    /// @endcode
    virtual V2NIMChatroomInfo getChatroomInfo() = 0;

    /// @brief 获取聊天室服务
    /// @return V2NIMChatroomService&
    /// @par 示例代码
    /// @code
    /// auto& chatroomService = client.getChatroomService();
    /// @endcode
    virtual V2NIMChatroomService& getChatroomService() = 0;

    /// @brief 获取存储服务
    /// @return V2NIMStorageService&
    /// @par 示例代码
    /// @code
    /// auto& storageService = client.getStorageService();
    /// @endcode
    virtual V2NIMStorageService& getStorageService() = 0;

    /// @brief 添加聊天室实例监听器
    /// @param listener 聊天室实例监听器
    /// @return void
    /// @par 示例代码
    /// @code
    /// V2NIMChatroomClientListener listener;
    /// listener.onChatroomStatus = [](V2NIMChatroomStatus status, nstd::optional<V2NIMError> error) {
    ///     // handle chatroom status
    /// };
    /// listener.onChatroomEntered = []() {
    ///     // handle chatroom entered
    /// };
    /// listener.onChatroomExited = [](nstd::optional<V2NIMError> error) {
    ///     // handle chatroom exited
    /// };
    /// listener.onChatroomKicked = [](V2NIMChatroomKickedInfo kickedInfo) {
    ///     // handle chatroom kicked
    /// };
    /// chatroomClient.addChatroomClientListener(listener);
    /// @endcode
    virtual void addChatroomClientListener(V2NIMChatroomClientListener listener) = 0;

    /// @brief 移除聊天室实例监听器
    /// @param listener 聊天室实例监听器
    /// @return void
    /// @par 示例代码
    /// @code
    /// V2NIMChatroomClientListener listener;
    /// // ...
    /// chatroomClient.addChatroomClientListener(listener);
    /// // ...
    /// chatroomClient.removeChatroomClientListener(listener);
    /// @endcode
    virtual void removeChatroomClientListener(V2NIMChatroomClientListener listener) = 0;
};

/// @internal
namespace internal {
class V2NIMChatroomSdkExport {
public:
    virtual nstd::optional<V2NIMError> init(const V2NIMInitOption& option) = 0;
    virtual void uninit() = 0;
    virtual nstd::shared_ptr<V2NIMChatroomClient> newInstance() = 0;
    virtual void destroyInstance(uint32_t instanceId) = 0;
    virtual nstd::shared_ptr<V2NIMChatroomClient> getInstance(uint32_t instanceId) = 0;
    virtual nstd::vector<nstd::shared_ptr<V2NIMChatroomClient>> getInstanceList() = 0;
    virtual void destroyAll() = 0;
};
inline V2NIMChatroomSdkExport* internalChatroomSdkExport{nullptr};
}  // namespace internal

inline V2NIMClient& V2NIMClient::get() {
    static const char* sdkFunctionV2InstanceGet = "v2_nim_instance_impl_get";
    static V2NIMClient* nimInstance{nullptr};
    if (nimInstance == nullptr) {
        void** instances = internal::callSdkFunction<void**>(internal::NIM_SDK_NAME, sdkFunctionV2InstanceGet);
        if (instances) {
            nimInstance = reinterpret_cast<V2NIMClient*>(instances[internal::V2_NIM_INTERNEL_INSTANCE]);
            internal::internalClientAntispamUtil =
                reinterpret_cast<internal::V2NIMClientAntispamUtilInternal*>(instances[internal::V2_NIM_INTERNEL_CLIENT_ANTI_SPAM_UTIL]);
            internal::internalConversationIdUtil =
                reinterpret_cast<internal::V2NIMConversationIdUtilInternal*>(instances[internal::V2_NIM_INTERNEL_CONVERSATION_ID_UTIL]);
            internal::internalMessageUtil = reinterpret_cast<internal::V2NIMMessageUtilInternal*>(instances[internal::V2_NIM_INTERNEL_MESSAGE_UTIL]);
        }
    }
    if (!nimInstance) {
        throw std::runtime_error("[V2NIMClient] get instance failed");
    }
    return *nimInstance;
}

inline nstd::optional<V2NIMError> V2NIMChatroomClient::init(const V2NIMInitOption& option) {
    static const char* sdkFunctionV2InstanceGet = "v2_nim_chatroom_instance_impl_get";
    if (internal::internalChatroomSdkExport == nullptr) {
        void** instances = internal::callSdkFunction<void**>(internal::NIM_CHATROOM_SDK_NAME, sdkFunctionV2InstanceGet);
        if (instances) {
            internal::internalChatroomSdkExport =
                reinterpret_cast<internal::V2NIMChatroomSdkExport*>(instances[internal::V2_NIM_CHATROOM_SDK_EXPORT]);
            internal::internalChatroomMessageUtil =
                reinterpret_cast<internal::V2NIMChatroomMessageUtilInternal*>(instances[internal::V2_NIM_CHATROOM_MESSAGE_UTIL]);
        }
    }
    if (!internal::internalChatroomSdkExport) {
        throw std::runtime_error("[V2NIMChatroomClient] init, internalChatroomSdkExport is null");
    }
    return internal::internalChatroomSdkExport->init(option);
}

inline void V2NIMChatroomClient::uninit() {
    if (!internal::internalChatroomSdkExport) {
        throw std::runtime_error("[V2NIMChatroomClient] uninit, internalChatroomSdkExport is null");
    }
    internal::internalChatroomSdkExport->uninit();
}

inline nstd::shared_ptr<V2NIMChatroomClient> V2NIMChatroomClient::newInstance() {
    if (!internal::internalChatroomSdkExport) {
        throw std::runtime_error("[V2NIMChatroomClient] newInstance, internalChatroomSdkExport is null");
    }
    return internal::internalChatroomSdkExport->newInstance();
}

inline void V2NIMChatroomClient::destroyInstance(uint32_t instanceId) {
    if (!internal::internalChatroomSdkExport) {
        throw std::runtime_error("[V2NIMChatroomClient] destroyInstance, internalChatroomSdkExport is null");
    }
    internal::internalChatroomSdkExport->destroyInstance(instanceId);
}

inline nstd::shared_ptr<V2NIMChatroomClient> V2NIMChatroomClient::getInstance(uint32_t instanceId) {
    if (!internal::internalChatroomSdkExport) {
        throw std::runtime_error("[V2NIMChatroomClient] getInstance, internalChatroomSdkExport is null");
    }
    return internal::internalChatroomSdkExport->getInstance(instanceId);
}

inline nstd::vector<nstd::shared_ptr<V2NIMChatroomClient>> V2NIMChatroomClient::getInstanceList() {
    if (!internal::internalChatroomSdkExport) {
        throw std::runtime_error("[V2NIMChatroomClient] getInstanceList, internalChatroomSdkExport is null");
    }
    return internal::internalChatroomSdkExport->getInstanceList();
}

inline void V2NIMChatroomClient::destroyAll() {
    if (!internal::internalChatroomSdkExport) {
        throw std::runtime_error("[V2NIMChatroomClient] destroyAll, internalChatroomSdkExport is null");
    }
    internal::internalChatroomSdkExport->destroyAll();
}
}  // namespace v2
#endif
